import random
import openpyxl as op
import math
import numpy as np
import copy
questionnaireCount = 121
###################产生2个以上随机整数###################
###################第一个数随机产生，第二个使用平均数求出###################
#count 数字的个数
#average 平均数
#begin 起始区间
#end 结束区间

def int_random_old (average, begin, end):

    numarr = [];

    while (1):

        num_first = random.randint(begin, end);
        num_second = average * 2 - num_first;

        if (num_second >= begin and num_second <= end):
            numarr.append(num_first);
            numarr.append(num_second);
            break
        # else:
            # print("num_second: " + str(num_second))

    return numarr;


def int_random (average, begin, end):

    #print "wzh_random"
    # numarr = [0 for x in range(2)];
    numarr = [];
    i = 0;
    # while (1):
 
    #   num_first = random.randrange(begin, end);
    num_first = random.randint(begin, end);

    #第二个数
    num_second = average * 2 - num_first;

    numarr.append(num_first);
  
   
    if (num_second >= begin and num_second <= end):  
        numarr.append(num_second);
    else:
        # 继续分配,分为三个数
        # 12 -1 ==11 ; 11+4 = 15
        # 15 -3 ==12 ; 12+5 = 17
        rest_val_temp = average * 3 - num_first

        restTwo = re_random(rest_val_temp, begin, end,average)
        numarr.extend(restTwo)

    return numarr;

def re_random (rest_val, begin, end,average):
    numarr = [];
    random_number = random.uniform(0,1)
    if rest_val <= 10:
        if random_number <= 0.5:
            # 大值与小值
            numarr.append(end)
            numarr.append(rest_val - end)
        else:
            # 平均分
            if (rest_val % 2) == 0:
                #偶数
                numarr.append(rest_val/2)
            else:
                #奇数
                numarr.append(rest_val//2)
                numarr.append(rest_val//2 + 1)
    else:
        rest_val2 = rest_val + average
        mean_temp =    rest_val2//3
        numarr.append(mean_temp)
        numarr.append(mean_temp)
        numarr.append(rest_val2-mean_temp*2)

    """     
    for i in range(len(numarr)):
        if numarr[i] > end: 
    """            

    return numarr;


###################产生随机数###################
###################第一个数随机产生，第二个使用平均数求出###################
#count 数字的个数
#average 平均数
#begin 起始区间
#end 结束区间
def float_random (count, average, begin, end):

    #print "wzh_random"
    numarr = [0 for x in range(2)];
    i = 0;
    while (1):
 
      num = random.uniform(begin, end);
      #取两位小数
      num_first = round(num, 2);

      #第二个数
      num_second = average * 2 - num_first;

      if (num_second >= begin and num_second <= end):
          numarr[i] = num_first;
          i = i + 1;
          numarr[i] = num_second;
          break

    return numarr;



###################写文件###################
def write_file (filename, content):
    fo = open (filename, "rb+");
    fo.write(bytes(content,'utf-8'));
    # fo.write(content);
    fo.close();

def show_list (list):
    for i in list:
        print(i, end=' ')
    
    t_sum = 0   
    t_average = 0.0

    t_sum = sum(list)
    t_average = round(t_sum/questionnaireCount,4)

    print("列表的和：" + str(t_sum))
    print("数量："+ str(len(list)));
    print("列表的平均数：" + str(t_average))

###################主函数调用产生整形随机数###################

# 接受4个参数：原数组, 最小值，最大值，和需要交换数据的长度
def generate_random_index_pairsAndExchange(list,min_index=0,max_index=50,item_length=10): 
    # 创建一个空的数组
    digitArray = []
    # 用一个循环，直到数组长度达到指定的数目
    while len(digitArray) < item_length:
        # 生成一个随机整数，范围在最小值和最大值之间
        num = random.randint(min_index, max_index)
        # 检查这个整数是否已经在数组中，如果不在，就添加到数组中
        if num not in digitArray:
            digitArray.append(num)
    # return digitArray

    lastIndex = len(list) - 1

    for num in digitArray:
        # print(num, lastIndex-num)
        swapPositions(list,num,lastIndex-num)


def dataExchange(list):
    # 生成一个1到4到随机数

    lastIndex = len(list) - 1
    num_begin = random.randint(1, 4);
    swapPositions(list,num_begin,lastIndex-num_begin)
    swapPositions(list,num_begin+1,lastIndex-num_begin-1)
    swapPositions(list,num_begin+2,lastIndex-num_begin-2)

    return list

def swapPositions(list, pos1, pos2):
     
    list[pos1], list[pos2] = list[pos2], list[pos1]
    return list

def test_random_int(averageParam,questionnaireCountParam,maxValue=5):
    
    realAverage = averageParam

    begin = math.floor(averageParam)
    end = math.ceil(averageParam)

    # 对平均数取整以后，要对平均数进行调整 5的话不出现begin=2, 3的话end =4
    average = round(realAverage)
    print(str(realAverage) + " >>>>>> " + str(average) )
    count = questionnaireCountParam
    # begin = 3; end = 4;
    numarr_count = 0;
    numarr = [0 for x in range(count)]

    
    print("begin："+ str(begin))
    print("end："+ str(end))

    # for i in range (count // 2):
    while(numarr_count < count):
        templist = int_random_old (average, begin, end)
        j = 0;
        for j in range (len(templist)):
            # numarr_count 有可能大于120
            if numarr_count < count:
                numarr[numarr_count] = templist[j]
                numarr_count += 1
            else:
                print('注意!!!!!: numarr_count: ' + str(numarr_count))
                break
    
    # print("修正前 ######：")
    # show_list (numarr)

    # 数据修正开始
    #if (count % 2) != 0:
     #   numarr[count-1] = random.randint(begin, end);

    t_average2 = round(sum(numarr)/count,4)
    # 平均数差值
    gapValue = round( realAverage - t_average2,4)
    if(  gapValue != 0):
        # 差了多少没有分配
        undistributed = round(count * gapValue,4)
        print("undistributed: " + str(undistributed))
        # 四舍五入取整
        undistributed = round(undistributed)

        # 分配到每个数里去,多退少补
        if(undistributed > 0):
            for x in range(len(numarr)):
                # undistributed 用完的时候跳出
                if(undistributed <= 0):
                    print("undistributed 剩余: " + str(undistributed))
                    break;
                if (numarr[x] < end):
                    numarr[x]  +=1
                    undistributed -=1
        else:
             for x in range(len(numarr)):
                # undistributed 用完的时候跳出
                if(undistributed >= 0):
                    print("undistributed 剩余: " + str(undistributed))
                    break
                if (numarr[x] > begin or numarr[x] == maxValue):
                    numarr[x]  -=1
                    undistributed +=1
    # 从小到大排序
    numarr = sorted(numarr)
    # 数据打乱
    # random.shuffle(numarr);

    # 排序以后在spss里分析，信度会过高，效度分析时关联性太强，无法显示KMO值，因此要稍微弄乱一点
    # 随机在前五 和 倒数前五个数中 分别抽取3个， 相互调换位置
    # 这里可以尝试,按照组的顺序, 从不同的位置开始交换数据, 这样每个组之间有一定差异, 同组的数据又比较相似 2023-1202
    
    numarr = dataExchange(numarr)
    return numarr

###################主函数调用产生实型随机数###################


def yieldIntRandNumbersByExpectedAverage(averageParam,questionnaireCountParam,maxValue=5):

    realAverage = averageParam
    begin = math.floor(averageParam)
    end = math.ceil(averageParam)

    # 对平均数取整
    average = round(realAverage)
 
    numarr_count = 0;
    numarr = []

    
    print("begin："+ str(begin))
    print("end："+ str(end))

#按照预期的平均值产生整形随机数
# test_random_int(3.6158);
# sys.exit(0)

# 期望值和实际满意度值都来自 HT酒店问卷.xlsx(sheet-result)
#expectList = [4.7678,3.6186,4.2126,4.645,3.6186,4.2086,4.3846,4.2955,3.9666,4.0586,4.2747,4.5946,4.2701,3.9326,4.2927,4.4814,3.9269,4.5673,4.2785,3.8283,4.4761,4.4532,4.4108]


def adjust_data(data, adjustment, min_value, max_value):
    if adjustment > 0:
        indices = [i for i in range(len(data)) if data[i] < max_value]
        while adjustment > 0 and indices:
            index = random.choice(indices)
            data[index] += 1
            adjustment -= 1
            indices = [i for i in range(len(data)) if data[i] < max_value]
    elif adjustment < 0:
        
        """  这行代码是一个列表推导式，用于创建一个包含满足条件的索引的列表。解释一下每部分的含义：

        indices: 这是要创建的列表的名称，它将包含满足条件的索引。

        [i for i in range(num_rows) if data[i] < max_value]: 这部分是列表推导式的核心。它有两部分：

        for i in range(num_rows): 这部分表示我们要迭代从0到num_rows - 1的所有可能的i值。这是一个遍历数据列的索引的循环。

        if data[i] < max_value: 这是条件部分。它过滤了那些满足条件 data[i] < max_value 的索引，即在数据列中找到小于max_value的元素对应的索引。

        所以，整个代码行的目的是创建一个包含数据列中小于max_value的元素对应的索引 """
        indices = [i for i in range(len(data)) if data[i] > min_value]
        while adjustment < 0 and indices:
            index = random.choice(indices)
            data[index] -= 1
            adjustment += 1
            indices = [i for i in range(len(data)) if data[i] > min_value]

def get_indices(data,pnParam,min_value,max_value):
    if pnParam == 1:
        indicesTemp = [i for i in range(len(data)) if data[i] < max_value]
    else:
        indicesTemp = [i for i in range(len(data)) if data[i] > min_value]
    return indicesTemp

def adjust_data_rhythm(dataParam, adjustment, min_value, max_value):

    data = copy.deepcopy(dataParam)

    if adjustment > 0:
        pn = 1
    else:
        pn = -1
    indices = get_indices(data,pn,min_value,max_value)

    print('可调整的元素总数:',len(indices))
   
    while abs(adjustment) >= len(indices):
        print('indices ',indices)
        print('adjustment #2 ',adjustment)
        # 需要调整的数据很大, 那么极值以内的数字都要加1
        for index_val in indices:
            data[index_val] += 1*pn
            adjustment -= 1*pn
            # indices.remove(index_val)
        # 更新极限值以内元素的索引
        indices = get_indices(data,pn,min_value,max_value)  
    
    data = rhythmical_skip_adjust(data, adjustment,indices)

    """ elif adjustment < 0:        
        indices = [i for i in range(len(data)) if data[i] > min_value]
        while adjustment < 0 and indices:
            index = random.choice(indices)
            data[index] -= 1
            adjustment += 1
            indices.remove(index) """

    # 确保方差不为0
    data = adjust_zero_array_variance(data,min_value, max_value)
            
    return data

def rhythmical_skip_adjust(arr_param, adjust_number,surplus_indices):
    """ print('第二阶段 arr_param',arr_param)
    print('第二阶段 总和',sum(arr_param))
    print('第二阶段 adjust_number',adjust_number)
    print('第二阶段 平均数',np.mean(arr_param))
    print('第二阶段 surplus_indices',surplus_indices)
    print('len',len(surplus_indices)) """

    if adjust_number > 0:
       pn = 1
    else:
       pn = -1
    # arr_length = len(arr_param)
    arr_length = len(surplus_indices)
    gap_count = arr_length - abs(adjust_number)

    action = None 
    modify_count = 0

    if (gap_count < arr_length//2) :      
      action = 'skip'
      modify_count = gap_count
    else:      
      action = 'modify'
      modify_count = abs(adjust_number)

    if (adjust_number ==0 or modify_count <= 0):
        return arr_param  # 如果要跳过的数量大于等于数组长度，直接返回原数组

    step_size = arr_length // (modify_count + 1)  # 计算跳过元素的步长
    remainder = arr_length % (modify_count + 1)  # 计算余数，用于平均分配余下的跳过元素
    
    # surplus_indices索引数组的(应当跳过的)索引值
    skip_or_mod_indexes_surplus = []
    for i in range(1, modify_count + 1):
        skip_index = i * step_size + min(i, remainder)
        skip_or_mod_indexes_surplus.append(skip_index)

    skip_or_mod_indexes = [surplus_indices[index] for index in skip_or_mod_indexes_surplus]

    result = copy.deepcopy(arr_param)

    if action == 'skip':
        for item_index in surplus_indices:
            if item_index in skip_or_mod_indexes:            
                print('跳过的元素索引',item_index)
                continue
            else:
                result[item_index] += 1*pn # 对其他元素加 1
        
        
    elif action == 'modify':
      for item_val in skip_or_mod_indexes:        
        result[item_val] += 1*pn
    return result

def adjust_zero_array_variance(arr, min_val, max_val):

    # 检查数组是否为空或只有一个元素
    if len(arr) <= 1:
        return arr
   
    # 循环直到数组的方差不为零
    while np.var(arr) == 0:
        # 随机选取数组中的两个元素的索引
        index1, index2 = np.random.choice(range(len(arr)), 2, replace=False)
        # 选取这两个索引对应的元素
        num1, num2 = arr[index1], arr[index2]

        # 确保选取的数在极限值范围内
        if min(num1, num2) > min_val and max(num1, num2) < max_val:
            # 对选取的两个元素进行调整
            arr[index1] = min(num1 + 1, max_val)
            arr[index2] = max(num2 - 1, min_val)

    return arr

def round_to_int_range(num, minimum, maximum):
    # 将 num 四舍五入成一个整数
    num = round(num)
    
    # 计算整数-1 和 整数+1 的值
    num_minus_one = num - 1
    num_plus_one = num + 1

    # 如果 num_minus_one 小于最小值，将其设为最小值
    if num_minus_one < int(minimum):
        num_minus_one = minimum    
    # 如果 num_plus_one 大于最大值，将其设为最大值
    if num_plus_one > int(maximum):
        num_plus_one = maximum
    
    # 返回 num_minus_one 和 num_plus_one
    return num_minus_one, num_plus_one
                
def generate_integersV1(average, n=150, minimum=1, maximum=5,sort_order_way=None):
    # 注意, minimum和maximum已是最终的限制, 初步生成数据的时候, 主要根据average确定最大值与最小值
    # numpy.random.randint() 函数用于生成指定范围内的随机整数, low：生成的随机数的最小值（包含）, high：生成的随机数的最大值（不包含）

    minval_real,maxval_real = round_to_int_range(average,minimum,maximum)
    # dataArr = np.random.randint(minval_real, maxval_real + 1, n)
    dataArr = np.random.randint(minimum, maximum + 1, n)

    adjustment = round(average * n) - np.sum(dataArr)
    # adjust_data(dataArr,adjustment,minval_real,maxval_real)
    adjust_data(dataArr,adjustment,minimum,maximum)


    numArrLength = len(dataArr)

    if sort_order_way == 'asc':
        # 从小到大排序
        dataArr = sorted(dataArr)
        # 随机抽取数组元素， 相互调换位置
        generate_random_index_pairsAndExchange(dataArr,0,math.floor(numArrLength/5),math.floor(numArrLength/15))
    elif sort_order_way == 'desc':
        # obj.sort()是方法，sorted(obj)是函数，前者永久改变列表次序，sorted生成一个新的列表
        # 降序
        dataArr = sorted(dataArr, reverse=True)
        # 随机抽取数组元素， 相互调换位置
        generate_random_index_pairsAndExchange(dataArr,0,math.floor(numArrLength/5),math.floor(numArrLength/15))
    elif sort_order_way == 'random':
        pass

    return dataArr

def generate_integersV2(n, minimum, maximum, average):
    std_dev = (maximum - minimum) / 6
    numbers = np.random.normal(loc=average, scale=std_dev, size=n)
    numbers = np.clip(numbers, minimum, maximum)
    return numbers.astype(int).tolist()


if __name__ == "__main__":
    # yieldIntRandNumbersByExpectedAverage(4.32,10)
  
    arr1 = generate_integersV1(3.5,150,1,5)
     
    sum_arr1 = np.sum(arr1)

    # 计算 arr1 和 arr2 的平均数
    mean_arr1 = np.mean(arr1)
    print('预期总和',150 * 3.5)
    print('数组1 ',arr1)
    print(f"arr1 的总和为 {sum_arr1} ")
    print(f"arr1 的平均数为 {mean_arr1} ")

    exit()

    perciveList = [2.5332,4.258,3.437,2.6852,4.1787,3.241,3.7591,3.6448,3.372,3.63,4.258,2.8037,3.9104,3.6703,3.8763,3.997,3.7745,3.1142,3.9463,3.6831,3.1383,3.2094,3.3453]

    bg = op.load_workbook(r"data_1.xlsx")    

    for colIndex in range(len(perciveList)):

        resultColum = test_random_int(perciveList[colIndex]) 
        # xlsx文件的的perciveList表
        sheet = bg["perciveList"]                             		 
        for row in range(len(resultColum)):						
            sheet.cell(row+1 , colIndex + 1, resultColum[row])	# sheet.cell(1,1,num_list[0])表示将num_list列表的第0个数据1写入到excel对应表格的第一行第一列
        bg.save("data_1.xlsx")            			# 对文件进行保存         


    # https://blog.csdn.net/l734971107/article/details/109635668